package com.ljxt.dt.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import javax.sql.DataSource;


/**
 *
 * @author 唐春劲
 * @version 1.0.0
 * @date 2020-04-26 15:19
 * @copyright 老九学堂
 */

public  class DaoUtil {
	public   static  DataSource dataSource = null;
	public  static void setDataSource(DataSource source){
		dataSource = source;
	}
	/**
	 * 得到连接的方法
	 * @return
	 * @throws Exception
	 */
	public   static Connection getConnection() throws Exception {
		if(dataSource == null){
			throw  new RuntimeException("连接池未加载,请检查监听器");
		}

		return DaoUtil.dataSource.getConnection();
	}
	/**
	 * 得到sql执行对象
	 * @param connection
	 * @param sql
	 * @return
	 * @throws Exception
	 */
	public  static   PreparedStatement getStatement(Connection connection,String sql,Object... parms) throws Exception {
		PreparedStatement statement = connection.prepareStatement(sql);
		if(parms.length !=0 && parms != null ) {
			for (int i = 0; i < parms.length; i++) {

				statement.setObject(i+1, parms[i]);
			}
		}

		return statement;

	}



	public   static  void close(Connection cnt, PreparedStatement statement,ResultSet rSet) {
		if(cnt != null) {
			try {
				cnt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(statement != null) {
			try {
				statement.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		if(rSet != null) {
			try {
				rSet.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * 根据数据库查询的结果得到一个对象
	 * @param <T>
	 * @param obj 传入返回的对象
	 * @param resultSet 传入当前的resultSet
	 * @return 返回一个已将值设置好的对象
	 * @throws Exception
	 */

	public  static  <T> T getObj(T obj,ResultSet resultSet) throws Exception{

		//得到传入进来的对象的class
		Class<?> clazz = obj.getClass();
		//得到该class的类列表
		BeanInfo bni = Introspector.getBeanInfo(clazz);
		//得到传入进来的所有的属性和对应的方法
		PropertyDescriptor[] propertyMethod =  bni.getPropertyDescriptors();

		//循环遍历得到的属性和方法
		for (int i = 0; i < propertyMethod.length; i++) {
			PropertyDescriptor propertyDescriptor = propertyMethod[i];

			String propertyName = propertyDescriptor.getDisplayName();

			//得到该属性的setter方法
			Method method = propertyDescriptor.getWriteMethod();
			//这里会得到该对象的class属性，但是class没有setter方法，所有返回null
			if(method == null) {
				continue;
			}
			//得到setter方法的形式参数类型的class
			Type[] types = method.getGenericParameterTypes();
			//由于是setter方法，则肯定会有一个形式参数
			//得到该参数的数据类型
			String typeName =  types[0].getTypeName().substring(types[0].getTypeName().lastIndexOf(".")+1);
			//得到当前setter方法对应的属性名
			String name = method.getName().substring(3);
			//通过以上数据将数据库对应的数据取出来并储存

			//得到当前方法对应的数据库字段名称
			String sqlName = getColumn(name);
			switch (typeName) {
				case "Integer":
					//得到对应属性名对应的setter方法

					method.invoke(obj,resultSet.getInt(sqlName));

					break;
				case "String":

					method.invoke(obj,resultSet.getString(sqlName));
					break;
				case "Date":

					method.invoke(obj,resultSet.getTimestamp(sqlName));
					break;
				case"Double":
					method.invoke(obj,resultSet.getDouble(sqlName));
					break;
				case"Float":
					method.invoke(obj,resultSet.getFloat(sqlName));
				default:
					method.invoke(obj,resultSet.getString(sqlName));
					break;
			}


		}

		return obj;
	}



	/**
	 * 查询数据库唯一值
	 * @param <T>
	 * @param sql 传入sql语句
	 * @param obj 传入返回的对象
	 * @param parms 传入参数
	 * @return 没找到返回 null 找到后返回数据库查询后的对象
	 */
	public   static <T> T  querySelectOnly(String sql,T obj, Object...parms) {
		Connection connection = null;
		PreparedStatement statement = null;
		ResultSet resultSet = null;


		try {
			connection = getConnection();
			statement = getStatement(connection, sql, parms);
			resultSet = statement.executeQuery();

			if(!resultSet.next()) {
				return null;
			}else {
				obj =  getObj(obj, resultSet);
			}


			return obj;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}finally {
			close(connection, statement, resultSet);
		}



	}

	/**
	 * 查询sql语句查询数据库匹配的所有
	 * @param <T>
	 * @param sql 传入sql语句
	 * @param obj 传入一个查询后转为的对象的对象
	 * @param parms 传入sql语句的参数
	 * @return 返回一个所有匹配的对集合
	 */
	public  static  <T> List<T>  querySelectAll(String sql,T obj, Object...parms) {
		Connection connection = null;
		PreparedStatement statement = null;
		ResultSet resultSet = null;
		List<T> list = new ArrayList<T>();

		//得到传进来对象的class
		Class<?> clazz = obj.getClass();

		try {
			connection = getConnection();

			statement = getStatement(connection, sql, parms);


			resultSet = statement.executeQuery();

			while(resultSet.next()) {

				//通过class创建传入进来的对象
				T temp = (T) clazz.newInstance();
				temp = getObj(temp, resultSet);
				list.add(temp);
			}
			if(list.size() == 0) {
				return null;
			}
			return list;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}finally {
			close(connection, statement, resultSet);
		}



	}

	/**
	 * 方法的重载  ，主要用于条件查询，传入一个含有所有sql参数的对象，得到所有匹配的数据
	 * @param obj  传入对象
	 * @param <T>
	 * @return
	 */
	public  static  <T> List<T>  querySelectAll(T obj) {
		//拼接sql语句

		String sql = "select * from ";
		//得到对象名
		String tableName = obj.getClass().toString();
		//得到数据库表名
		tableName = getTableName(tableName);

		sql += tableName;
		//得到对应的sql语句和参数
		Map<String,List<Object>> map = getSql(obj,sql);
		//用来储存sql对应的参数
		List<Object> sqlParms = new ArrayList<>();
		for (String s: map.keySet()) {
			sql = s;
			sqlParms = map.get(s);
		}


		//调用重载的方法
		return  querySelectAll(sql,obj,sqlParms.toArray());
	}


	/**
	 * 方法的重载  ，主要用于条件查询，传入一个含有所有sql参数的对象，得到所有匹配的当前分页数据
	 *
	 * @param obj 传入含有条件的对象
	 * @param pageIndex  传入当前的页数
	 * @param pageSize 传入最大页数
	 * @param <T>
	 * @return
	 */
	public   static <T> List<T>  querySelectAll(T obj ,Integer pageIndex,Integer pageSize,Object...params) {
		//拼接sql语句

		String sql = "select * from ";


		//得到对象名
		String tableName = obj.getClass().toString();
		//得到数据库表名
		tableName = getTableName(tableName);

		sql += tableName;

		//得到对应的sql语句和参数
		Map<String,List<Object>> map = getSql(obj,sql);
		//用来储存sql对应的参数
		List<Object> sqlParms = new ArrayList<>();
		for (String s: map.keySet()) {

			sql = s;
			sqlParms = map.get(s);
		}
		//判断是否传入了不定长参
		if(params!=null && params.length >=1){
			//这里的规则是如果要用自己的sql语句则将sql 语句传入到最后一个元素,前面的则是该语句的参数
			sql = (String)params[params.length-1];
			List<Object> temp = new ArrayList<Object>(Arrays.asList(params));
			temp.remove(temp.size()-1);

			sqlParms = temp;
		}
		sql = sql+" limit "+(pageIndex-1) * pageSize + ", "+pageSize;
		//调用重载的方法
		return  querySelectAll(sql,obj,sqlParms.toArray());
	}






	/**
	 * 查询数量
	 * @param sql 传入sql
	 * @param parms 传入参数
	 * @return
	 */
	public   static int querySelectCount(String sql,Object...parms) {

		Connection connection = null;
		PreparedStatement statement = null;
		ResultSet resultSet = null;

		try {
			connection = getConnection();
			statement = getStatement(connection, sql, parms);
			resultSet = statement.executeQuery();
			if(resultSet.next()) {

				return resultSet.getInt(1);
			}
		} catch (Exception e) {

			e.printStackTrace();
			return -1;
		}finally {
			close(connection, statement, resultSet);
		}
		return 0;


	}


	/**
	 *
	 * @param <T>
	 * @param obj
	 * @return
	 */
	public  static  <T> int querySelectCount(T obj) {
		String sql = "select count(*) from ";
		//得到表名
		String tableName = getTableName(obj.getClass().toString());
		//拼接表
		sql += tableName;
		//得到对应的sql语句和参数
		Map<String,List<Object>> map = getSql(obj,sql);
		//用来储存sql对应的参数
		List<Object> sqlParms = new ArrayList<>();
		for (String s: map.keySet()) {
			sql = s;
			sqlParms = map.get(s);
		}

		//调用重载的方法

		return querySelectCount(sql,sqlParms.toArray());
	}







	/**
	 * 根据sql查询某个字段的值
	 * @param sql
	 * @param parms
	 * @return
	 */
	public   static String querySelectString(String sql,Object...parms) {
		Connection connection = null;
		PreparedStatement statement = null;
		ResultSet resultSet = null;

		try {
			connection = getConnection();
			statement = getStatement(connection, sql, parms);
			resultSet = statement.executeQuery();
			if(resultSet.next()) {
				return resultSet.getString(1);
			}
		} catch (Exception e) {

			e.printStackTrace();
			return "";
		}finally {
			close(connection, statement, resultSet);
		}
		return "";
	}



	/**
	 * 根据sql查询某个字段的所有匹配的值
	 * @param sql
	 * @param parms
	 * @return
	 */
	public  static  List<String> querySelectStringAll(String sql,Object...parms) {
		Connection connection = null;
		PreparedStatement statement = null;
		ResultSet resultSet = null;
		List<String> lists = new ArrayList<String>();
		try {
			connection = getConnection();
			statement = getStatement(connection, sql, parms);
			resultSet = statement.executeQuery();

			while(resultSet.next()) {
				lists.add(resultSet.getString(1));
			}

			return lists;
		} catch (Exception e) {

			e.printStackTrace();
			return null;
		}finally {
			close(connection, statement, resultSet);
		}
	}


	/**
	 * 更新数据库数据
	 * @param sql 传入sql语句
	 * @param parms 传入条件
	 * @return
	 */
	public   static int updateDate(String sql,Object...parms) {
		Connection connection = null;
		PreparedStatement statement = null;

		try {
			connection = getConnection();
			statement = getStatement(connection, sql, parms);

			return  statement.executeUpdate();
		} catch (Exception e) {
			// TODO Auto-generated catch block

			e.printStackTrace();
			return -1;
		}finally {
			close(connection, statement, null);
		}


	}

	/**方法重载
	 * 根据传入一个对象新增数据库数据
	 * @param <T>
	 * @param obj 传入一个含有数据的对象
	 * @return
	 */
	public  static  <T> int updateDate(T obj) {

		//得到sql语句和值
		List<Object> list  = 	getSqlAndValue(obj);

		return updateDate((String)list.get(0),(Object[])list.get(1) );

	}


	/**
	 * 通过传入进来的对象生成insert语句以及对应的值的数组也就是Object...parms
	 * @param <T>
	 * @param obj
	 * @return 返回一个list 第一个是Object 类的sql语句，第二个是Object类的 Object[]参数
	 * 也就是不定长参 params 得到后需要将第一个强转为String 第二强转为 Object[]
	 */
	public   static <T> List<Object> getSqlAndValue(T obj){
		//获取对应的class类

		Class<?> clazz = obj.getClass();

		//获取里面的属性

		Field[] fields = clazz.getDeclaredFields();


		//获得表名
		String tableName = getTableName(clazz.getName());

		//定义返回的sql语句
		String sql = "insert into "  +tableName +" (";
		String valueSql = "values (";
		//用来储存对应的sql语句的值
		List<Object> list = new ArrayList<Object>();
		//定义返回的list
		List<Object> returnList = new ArrayList<Object>();

		//获得所有属性
		for (int i = 0; i < fields.length; i++) {
			//得到当前的filed
			Field field = fields[i];
			//设置访问权限
			field.setAccessible(true);
			//得到属性名
			String filedName =  field.getName();
			//得到属性对应的值
			Object o = null;
			try {
				 o = field.get(obj);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			//判断值是否是null
			if(o != null){
				//得到该字段的数据库名
				String sqlName = getColumn(filedName);
				//将该名字拼入sql语句
				sql+=sqlName+" ,";
				//将值拼入
				valueSql+="?,";
				list.add(o);
			}

		}
		sql = sql.substring(0,sql.length()-1)+")";
		valueSql = valueSql.substring(0,valueSql.length()-1)+")";

		returnList.add(sql+valueSql);
		returnList.add(list);
		return returnList;
	}

	/**
	 * 私有的方法 得到数据库的表名
	 * @param tableName
	 * @return
	 */
	private  static  String getTableName(String tableName) {
		tableName = tableName.substring(tableName.lastIndexOf(".")+1);
		return getColumn(tableName);

	}


	/**
	 * 得到数据库列名
	 * @param name
	 * @return
	 */
	private   static String getColumn(String name){


		String result= "";
		for (int i = 0; i < name.length(); i++) {

			if (name.charAt(i) >= 'A' && name.charAt(i) <= 'Z') {

				char c = name.charAt(i);
				c+=32;
				if(i == 0) {
					result += c;
					continue;
				}else {
					result += "_" + c;
				}

			} else {
				result += name.charAt(i);
			}

		}


		return  result;
	}


	/**
	 * 用来通过传入的对象获得sql语句以及对应的参数
	 * @param obj
	 * @param sql
	 * @param <T>
	 * @return
	 */
	public  static   <T>  Map<String,List<Object>> getSql(T obj, String sql){
		sql += 	" where 1=1 and ";

		Map<String,List<Object>> map = new HashMap<>();
		//用来储存sql语句参数
		List<Object> sqlParms = new ArrayList<>();
		//得到传入进来的对象的所有属性
		BeanInfo beanInfo = null;
		try {
			beanInfo= Introspector.getBeanInfo(obj.getClass());
		} catch (IntrospectionException e) {
			e.printStackTrace();

		}

		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		//循环遍历得到的所有的属性
		for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
			//得到属性名
			String name = propertyDescriptor.getName();
			if("startTime".equals(name) || "endTime".equals(name)){
				continue;
			}
			//得到它的getter方法
			Method getMethod = propertyDescriptor.getReadMethod();
			//调用get方法得到值
			Object type = null;
			try {
				type = getMethod.invoke(obj);
			} catch (Exception e) {
				e.printStackTrace();

			}
			//排除class的类属性和值为空的属性

			if("class".equals(name) ||type == null || type.toString().isEmpty()) {
				continue;
			}

			//得到数据库列名
			String column = getColumn(name);

			sql+= column + " = ? ";

			sql += "  and  ";
			sqlParms.add(type);


		}

		if(!sql.contains("?")){
			sql = sql.replaceAll("and","");
		}else {
			sql = sql.substring(0,sql.lastIndexOf("?")+1);
		}

			map.put(sql,sqlParms);
		return map;
	}



	public  static   Map<String,List<Object>> fuzzySql(String sql,List<Object> params){
		sql = sql.replaceAll("="," like ");

		List<Object> tempList = new ArrayList<>();
		for (Object o:params) {
			tempList.add("%"+o+"%");
		}
		Map<String,List<Object>> map = new HashMap<>();
		map.put(sql,tempList);
		return map;
	}
}